package impl

import (
	"fmt"
	"time"

	"hundsun.com/hsl/hschain/common/log/logging"
	"hundsun.com/hsl/hschain/protos/ledger"
)

const (
	// TimeInterval 定时间隔
	TimeInterval = 10
	// DefaultBatchBlockExecResultCount 批量写入区块数
	DefaultBatchBlockExecResultCount = 2
)

func (chain *BlockChain) startCommitTask() {
	log := chain.log
	//设置定时器间隔为10毫秒
	duration := time.Duration(TimeInterval) * time.Microsecond
	t := time.NewTimer(duration)
	for {
		select {
		case <-chain.ctx.Done():
			log.Infof("startCommitTask exit...")
			return
		case <-t.C:
			t.Reset(duration)
			chain.commitJob()
		}
	}
}

func (chain *BlockChain) commitJob() {
	// commit时候需要从本地缓存中取出执行结果, 存入db之后将其从本地缓存中删除
	exedBlk := chain.getExecResultsCache()
	if len(exedBlk) > 0 {
		for _, eb := range exedBlk {
			if eb.GetBlock() == nil ||
				eb.GetBlockExecResult() == nil {
				continue
			}
			err := chain.CommitExecutedBlock(eb)
			if err != nil {
				return
			}
		}
	}
}

// CommitExecutedBlock 提交执行区块
func (chain *BlockChain) CommitExecutedBlock(eb *ledger.ExecutedBlock) error {
	blockNum := eb.GetBlockExecResult().GetBlockNumber()
	mqLog := chain.log.WithField(logging.LogFieldBlockNumber, blockNum)
	start := time.Now()
	var errstr string

	mqLog.Debugf("start store block ExecRst to db")
	defer func() {
		mqLog.Infof("end store block ExecRst to db tx count %d, cost time %v, err %s",
			len(eb.GetBlockExecResult().GetTransactionExecResults()), time.Since(start), errstr)
	}()

	// 1. 先是账本数据提交
	// TODO 后续可以对账本数据中kv进行裁剪
	eblk := &ledger.ExecutedBlock{
		Block:           eb.GetBlock(),
		BlockExecResult: eb.GetBlockExecResult()}
	err := chain.LedgerDataCommit(eblk)
	if err != nil {
		errstr = fmt.Sprintf("LedgerDataCommit fail %v", err)
		return err
	}

	// 2. 提交状态数据
	err = chain.StateDataCommit(eb.GetBlockExecResult().GetStateHash())
	if err != nil {
		errstr = fmt.Sprintf("StateDataCommit fail %v ", err)
		return err
	}

	// 3. 提交数据后处理
	chain.storeFinishProc(eb)
	return nil
}

// LedgerDataCommit 账本数据提交
func (chain *BlockChain) LedgerDataCommit(eb *ledger.ExecutedBlock) error {
	if eb == nil {
		return nil
	}
	blockNum := eb.GetBlockExecResult().GetBlockNumber()
	mqLog := chain.log.WithField(logging.LogFieldBlockNumber, blockNum)
	start := time.Now()
	errstr := ""

	mqLog.Debugf("LedgerDataCommit start store block ExecRst to db")
	defer func() {
		mqLog.Infof("LedgerDataCommit end store block ExecRst to db tx count %d cost time %v, err %s ",
			len(eb.GetBlockExecResult().GetTransactionExecResults()), time.Since(start), errstr)
	}()

	// store block exec result
	err := chain.StoreLedgerData(eb)
	if err != nil {
		errstr = fmt.Sprintf("LedgerDataCommit store ledger data fail err=%v ", err)
		return err
	}

	return nil
}
